﻿using Lextm.SharpSnmpLib;
using Lextm.SharpSnmpLib.Messaging;
using Lextm.SharpSnmpLib.Security;
using SnmpForm.ApSearch;
using SnmpForm.DbWrapper.DbDataTypes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace SnmpForm.ApSearch
{
    class RequestTask
    {
        private List<SearchTarget> listTarget = null;
        int nProgress = 0;
        public int GetProgress()
        {
            return nProgress;
        }
        Socket udp = null;
        byte[] packetRequest = null;

        long nTotalHosts = 0;
        long nRequestedHosts = 0;
        //工作状态
        int nTargetIndex = 0;   //当前扫描的网段
        string strAddIndex = null; //网段中的具体一个ip

        byte [] buffer = new byte[8096];//接收缓冲区

        public RequestTask(List<SearchTarget> list)
        {
            listTarget = list;
            udp = CreateAvailableSocket();
            if (list != null)
            {
                foreach (SearchTarget target in list)
                {
                    nTotalHosts += IPAddressExtend.IpInterval(target.from, target.to) + 1;
                }
            }
        }

        public void ResetTask()
        {
            nTargetIndex = 0;
            strAddIndex = null;
        }

        private Socket CreateAvailableSocket()
        {
            Socket udp = new Socket(AddressFamily.InterNetwork,
                SocketType.Dgram,
                ProtocolType.Udp);

            //微软socketbug，UDP连接RESET
            //UDP 远程主机强迫关闭了一个现有的连接。
            uint IOC_IN = 0x80000000;
            uint IOC_VENDOR = 0x18000000;
            uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12;
            udp.IOControl((int)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null);

            return udp;
        }

        private List<string> listResult = new List<string>();

        private static readonly NumberGenerator RequestCounter = new NumberGenerator(int.MinValue, int.MaxValue);

        public bool Recv(int timeout = 0)
        {
            return Recv(udp, timeout);
        }
        private bool Recv(Socket udp, int timeout = 100)
        {
            bool bHaveMsg=false;
            if (udp.Poll(timeout, SelectMode.SelectRead))
            {
                try
                {
                    EndPoint remote = new IPEndPoint(IPAddress.Any, 0);
                    var count = udp.ReceiveFrom(buffer, ref remote);

                    UserRegistry registry = new UserRegistry();
                    ISnmpMessage response = MessageFactory.ParseMessages(buffer, 0, count, registry)[0];

                    RecvEvent(remote, response);
                }
                catch (SocketException ex)
                {
                    //微软socketbug，UDP连接RESET
                }
                catch (Exception ex)
                {
                }
                
                bHaveMsg = true;
            }
            else
            {
                bHaveMsg = false;
            }
            return bHaveMsg;
        }

        public virtual void RecvEvent(EndPoint remote, ISnmpMessage response)
        {
            foreach (Variable v in response.Pdu().Variables)
            {
                Console.WriteLine(remote.ToString());
                Console.WriteLine(v);

                DbWrapper.DbDataTypes.Ap apinfo = new Ap();
                apinfo.ip = ((IPEndPoint)remote).Address.ToString();
                apinfo.mac = v.Data.ToString();
            }
        }

        byte[] PackScanMsg()
        {
            List<Variable> vList = new List<Variable>();
            Variable test = null;
            //Ap.Common.GetApMac
            test = new Variable(new ObjectIdentifier(".1.3.6.1.4.1.16535.1.5.0"));
            vList.Add(test);

            GetRequestMessage request = new GetRequestMessage(RequestCounter.NextId, VersionCode.V1, new OctetString("public"), vList);

            byte[] bytes = request.ToBytes();
            return bytes;
        }

        public bool SendTask(Socket udp, byte [] packetRequest, int timeout=100)
        {
            bool bFinish = false;
            if (udp.Poll(timeout, SelectMode.SelectWrite))
            {
                if (nTargetIndex < listTarget.Count)
                {
                    SearchTarget target = listTarget[nTargetIndex];
                    if (strAddIndex == null)
                    {
                        strAddIndex = target.from;
                    }
                    IPAddress addrIndex = IPAddress.Parse(strAddIndex);
                    IPAddress addrTo = IPAddress.Parse(target.to);
                    if (IPAddressExtend.LessOrEquals(addrIndex, addrTo))
                    {
                        IPEndPoint _receiver = new IPEndPoint(addrIndex, 161);
                        udp.SendTo(packetRequest, 0, _receiver);
                        nRequestedHosts++;

                        addrIndex = IPAddressExtend.IPAdd(addrIndex);
                        strAddIndex = addrIndex.ToString();
                    }
                    else
                    {
                        nTargetIndex++;
                        strAddIndex = null;
                    }
                }
                else
                {
                    bFinish = true;
                }
            }
            return bFinish;
        }

        public virtual void TaskPre()
        {
            packetRequest = PackScanMsg();
        }

        public void TaskWork()
        {
            while (true)
            {
                if (Recv(udp))
                {
                }
                else if (SendTask(udp, packetRequest, 10))
                {
                    //所有主机都发送请求
                    break;
                }
                else
                {
                }
                int nOldProgress = 0;
                nProgress = (int)(nRequestedHosts * 100 / nTotalHosts);
                if (nProgress > nOldProgress)
                {
                    nOldProgress = nProgress;
                    ProgessChangeEvent();
                }
            }
        }

        public virtual void TaskPost()
        {
            //发送完所有的请求后，等待处理消息一段时间。
            while (true)
            {
                if (Recv(udp, 5000 * 1000))
                {
                }
                else
                {
                    break;
                }
            }
        }

        public virtual void ProgessChangeEvent()
        {
        }

        public virtual void TaskMain()
        {
            TaskPre();
            TaskWork();
            TaskPost();
        }

        public void TaskFunction()
        {
            TaskMain();
        }
    }
}
